package com.cryptape.cita_wallet.service;

import android.text.TextUtils;
import android.util.Log;

import com.cryptape.cita_wallet.CytonApplication;
import com.cryptape.cita_wallet.activity.transfer.EntrustNewTransferPresenter;
import com.cryptape.cita_wallet.activity.transfer.EntrustTransferPresenter;
import com.cryptape.cita_wallet.bean.DepositData;
import com.cryptape.cita_wallet.constant.ConstantUtil;
import com.cryptape.cita_wallet.item.Token;
import com.cryptape.cita_wallet.item.response.EthTransaction;
import com.cryptape.cita_wallet.item.transaction.RestTransaction;
import com.cryptape.cita_wallet.service.http.HttpService;
import com.cryptape.cita_wallet.util.NumberUtil;
import com.cryptape.cita_wallet.util.db.DBWalletUtil;
import com.cryptape.cita_wallet.util.ether.EtherUtil;
import com.google.gson.Gson;

import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

import okhttp3.Call;
import okhttp3.Request;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
 * Created by dengming on 13/2/20.
 */

public class DepositQueryService {


    public static Observable<DepositData> queryDepositData() {
        final Token HToken = new Token();
        HToken.setChainId("-105");
        HToken.symbol = ConstantUtil.HMIO_TOKEN_NAME;
        HToken.name = ConstantUtil.HMIO_TOKEN_NAME;
        HToken.decimals = 18;
        HToken.setContractAddress(ConstantUtil.getHToken());

        final Token MToken = new Token();
        MToken.name = ConstantUtil.getMGPName();
        MToken.symbol = ConstantUtil.getMGPName();
        MToken.decimals = 18;
        MToken.setContractAddress(ConstantUtil.getMGPToken());
        MToken.setChainId(ConstantUtil.MGP_CHAIN_ID);

        return Observable.fromCallable(new Callable<String>() {
            @Override
            public String call() throws Exception {
                List<Type> datas = ContractService.lockAddress();
                if (datas != null && datas.size() >= 1) {
                    String address = ((Address) datas.get(0)).getValue();
                    return address;
//                    if (!TextUtils.isEmpty(address) && !address.equals("0x0000000000000000000000000000000000000000")) {
//                        return address;
//                    }
                }
                return null;
            }
        }).subscribeOn(Schedulers.newThread())
                .flatMap(new Func1<String, Observable<DepositData>>() {
                             @Override
                             public Observable<DepositData> call(String address) {
                                 Log.e("MainActiviy", "DepositQueryService: address=" + address);
                                 DepositData depositData = new DepositData();
                                 if (!TextUtils.isEmpty(address) && !address.equals("0x0000000000000000000000000000000000000000")) {
                                     depositData.setLockAddress(address);
                                 }
                                 if (TextUtils.isEmpty(address)) {
                                     depositData.setCode(-1);
                                 }
                                 if (!TextUtils.isEmpty(address) && !address.equals("0x0000000000000000000000000000000000000000")) {
                                     List<Type> datas = ContractService.orderDetail(address);
                                     if (datas != null && datas.size() > 1) {
                                         String type = null;
                                         BigInteger lockType = ((Uint256) datas.get(0)).getValue();
                                         BigInteger natureType = (BigInteger.ONE);
                                         BigInteger mixType = (new BigInteger("2"));
                                         BigInteger emptyType = (new BigInteger("3"));
                                         if (lockType.compareTo(natureType) == 0) {
                                             type = EntrustTransferPresenter.TYPE_LOCK_NATURE;
                                         } else if (mixType.compareTo(lockType) == 0) {
                                             type = EntrustTransferPresenter.TYPE_LOCK_MIX;
                                         } else if (emptyType.compareTo(lockType) == 0) {
                                             type = EntrustTransferPresenter.TYPE_LOCK_EMPTY;
                                         }
                                         Log.e("MainActivity", "DepositQueryService: type=" + type);
                                         if (!TextUtils.isEmpty(type)) {
                                             depositData.setAppend(true);
                                         }
                                         depositData.setType(TextUtils.isEmpty(type) ? EntrustTransferPresenter.TYPE_LOCK_NATURE : type);
                                         BigInteger balance = ((Uint256) datas.get(1)).getValue();
                                         double lockNum = NumberUtil.getEthFromWei(balance);
                                         depositData.setLockNum(lockNum);

                                         if (mixType.compareTo(lockType) == 0) {
                                             BigInteger leftTime = ((Uint256) datas.get(2)).getValue();
                                             depositData.setLeftTime(leftTime);
                                         }
                                     }
                                     if (datas == null) {
                                         depositData.setCode(-1);
                                     }
//                                     ContractService.lockbalance(address);
//                                     List<Type> lockbalance = ContractService.lockbalance(address);
//                                     if (lockbalance != null && lockbalance.size() >= 1) {
//                                         BigInteger balance = ((Uint256) lockbalance.get(0)).getValue();
//                                         double lockNum = NumberUtil.getEthFromWei(balance);
//                                         depositData.setLockNum(new BigDecimal(String.valueOf(lockNum)+));
//                                     }
                                 }
                                 List<Type> aBalance = ContractService.aTokenBalance();
                                 if (aBalance != null && aBalance.size() > 0) {
                                     BigInteger balance1 = ((Uint256) aBalance.get(0)).getValue();
                                     Log.e("MainActivity", "A Balance:" + balance1.toString());
                                     depositData.setaTokenBalance(balance1);
                                 }
                                 if (aBalance == null) {
                                     depositData.setCode(-1);
                                 }
                                 List<Type> bBalance = ContractService.bTokenBalance();
                                 if (bBalance != null && bBalance.size() > 0) {
                                     BigInteger balance2 = ((Uint256) bBalance.get(0)).getValue();
                                     Log.e("MainActivity", "B Balance:" + balance2.toString());
                                     depositData.setbTokenBalance(balance2);
                                 }
                                 if (bBalance == null) {
                                     depositData.setCode(-1);
                                 }
                                 return Observable.just(depositData);
                             }
                         }
                ).subscribeOn(Schedulers.newThread())
                .flatMap(new Func1<DepositData, Observable<DepositData>>() {
                    @Override
                    public Observable<DepositData> call(DepositData depositData) {

                        if (depositData.getaTokenBalance() == null || depositData.getaTokenBalance().compareTo(BigInteger.ZERO) == 0) {
                            List<RestTransaction> datas = getEtherPendingTransactionList(MToken);
//                            List<RestTransaction> datas = getTransactions(MToken);
                            if (datas != null && datas.size() > 0) {
                                Log.e("MainActivity", "has unpending A:");
                                depositData.setaPendingBalance(new BigInteger(datas.get(0).value));
                            }
                        }
                        if (depositData.getaTokenBalance() == null || depositData.getaTokenBalance().compareTo(BigInteger.ZERO) == 0) {

                            List<RestTransaction> datas = getEtherPendingTransactionList(HToken);
//                            List<RestTransaction> datas = getTransactions(HToken);
                            if (datas != null && datas.size() > 0) {
                                Log.e("MainActivity", "has unpending B:");
                                depositData.setbPendingBalance(new BigInteger(datas.get(0).value));
                            }
                        }
                        if (depositData.getaTokenBalance().compareTo(BigInteger.ZERO) > 0
                                || depositData.getaPendingBalance().compareTo(BigInteger.ZERO) > 0
                                || depositData.getbTokenBalance().compareTo(BigInteger.ZERO) > 0
                                || depositData.getbPendingBalance().compareTo(BigInteger.ZERO) > 0) {
                            depositData.setStep(EntrustNewTransferPresenter.TYPE_CURRENT_STEP_LOCK);
                        }
                        if ((depositData.getaTokenBalance().compareTo(BigInteger.ZERO) > 0
                                || depositData.getbPendingBalance().compareTo(BigInteger.ZERO) > 0) &&
                                (depositData.getbTokenBalance().compareTo(BigInteger.ZERO) > 0
                                        || depositData.getbPendingBalance().compareTo(BigInteger.ZERO) > 0)
                                ) {
                            if (TextUtils.isEmpty(depositData.getType()))
                                depositData.setType(EntrustNewTransferPresenter.TYPE_LOCK_MIX);
                        } else if (depositData.getbPendingBalance().compareTo(BigInteger.ZERO) > 0
                                || depositData.getbTokenBalance().compareTo(BigInteger.ZERO) > 0) {
                            if (TextUtils.isEmpty(depositData.getType()))
                                depositData.setType(EntrustNewTransferPresenter.TYPE_LOCK_EMPTY);
                        } else if (depositData.getaPendingBalance().compareTo(BigInteger.ZERO) > 0
                                || depositData.getaTokenBalance().compareTo(BigInteger.ZERO) > 0) {
                            if (TextUtils.isEmpty(depositData.getType()))
                                depositData.setType(EntrustNewTransferPresenter.TYPE_LOCK_NATURE);
                        }
//                        depositData.getAPe
                        return Observable.just(depositData);
                    }
                }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }


    private static List<RestTransaction> getEtherPendingTransactionList(Token token) {
        return null;
        //        Wallet wallet = DBWalletUtil.getCurrentWallet(CytonApplication.getInstance());
//        String chainId = EtherUtil.getEtherId();
//        List<RpcTransaction> itemList = DBEtherTransactionUtil.getAllTransactionsWithToken(CytonApplication.getInstance(), chainId, token.getContractAddress());
//        List<RestTransaction> list = new ArrayList<>();
//        if (itemList.size() > 0) {
//            Iterator<RpcTransaction> iterator = itemList.iterator();
//            while (iterator.hasNext()) {
//                RpcTransaction dbItem = iterator.next();
//                if (!wallet.address.equals(dbItem.from) || !ConstantUtil.DEPOSIT_CONTRACT.equals(dbItem.to)) {
//                    continue;
//                }
//                if (dbItem.status != 2) {
//                    continue;
//                }
//
//                if (new BigInteger(dbItem.value).compareTo(BigInteger.ZERO) == 0) {
//                    continue;
//                }
//                list.add(new RestTransaction(dbItem));
//            }
////            for (RpcTransaction item : itemList)
////            {
////            }
//        }
//        Collections.sort(list, (o1, o2) -> o2.getDate().compareTo(o1.getDate()));
//        return list;
    }

    public static Observable<List<RestTransaction>> getHavePendingTransactions(Token token) {
        return HttpService.getEtherERC20TransactionList(CytonApplication.getInstance(), token)
                .flatMap(new Func1<List<RestTransaction>, Observable<List<RestTransaction>>>() {
                    @Override
                    public Observable<List<RestTransaction>> call(List<RestTransaction> orginTransactions) {
                        if (orginTransactions == null || orginTransactions.size() == 0) {
                            return Observable.just(null);
                        }
                        List<RestTransaction> contractTransaction = new ArrayList<>();
                        for (RestTransaction transaction : orginTransactions) {
                            if (!transaction.to.equals(ConstantUtil.getDEPOSITCONTRACT())) {
                                continue;
                            }
                            if (transaction.status == 0) {
                                continue;
                            }
                            if (new BigInteger(transaction.value).compareTo(BigInteger.ZERO) == 0) {
                                continue;
                            }
                            contractTransaction.add(transaction);
                        }
                        return Observable.just(contractTransaction);
                    }
                }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    private static List<RestTransaction> getTransactions(Token token) {
        String address = DBWalletUtil.getCurrentWallet(CytonApplication.getInstance()).address;
        String ethUrl = String.format(EtherUtil.getEtherERC20TransactionUrl(), token.getContractAddress(), address, 0, 100);

        final Request ethRequest = new Request.Builder().url(ethUrl).build();
        Call ethCall = HttpService.getHttpClient().newCall(ethRequest);
        try {
            EthTransaction response = new Gson().fromJson(ethCall.execute().body().string(), EthTransaction.class);
            List<RestTransaction> orginTransactions = response.result;
            if (orginTransactions == null || orginTransactions.size() == 0) {
                return null;
            }
            for (RestTransaction item : orginTransactions) {
                item.chainName = ConstantUtil.ETH_MAINNET;
                item.value = (NumberUtil.divideDecimalSub(new BigDecimal(item.value), token.decimals));
                item.symbol = token.getSymbol();
                item.nativeSymbol = ConstantUtil.ETH;
            }
            List<RestTransaction> contractTransaction = new ArrayList<>();
            for (RestTransaction transaction : orginTransactions) {
                if (!transaction.to.equals(ConstantUtil.getDEPOSITCONTRACT())) {
                    continue;
                }
                if (transaction.status != 2) {
                    continue;
                }
                if (new BigInteger(transaction.value).compareTo(BigInteger.ZERO) == 0) {
                    continue;
                }
                contractTransaction.add(transaction);
                return contractTransaction;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
